﻿using System;
using System.Diagnostics;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using NAnt.Core;
using NAnt.Core.Attributes;
using NAnt.Core.Types;
using System.Data.OleDb;

namespace NAntExt.Tasks
{
    /// <summary>
    /// The task will query for running processes which match the inputs and
    /// wait for them to finish up until the timeout.
    /// </summary>
    [TaskName("process-wait-while-running")]
    public class ProcessWaitWhileRunningTask : ProcessTaskBase
    {

        #region Property QueryFrequency

        private TimeSpan _queryFrequency = System.TimeSpan.FromSeconds(3);

        /// <summary>
        /// The length of time to sleep in between requests to query for running
        /// processes.
        /// Default is 3 seconds.
        /// </summary>
        [TaskAttribute("queryFrequency")]
        [StringValidator(AllowEmpty = false)]
        public string QueryFrequency
        {
            get
            {
                return _queryFrequency.ToString();
            }
            set
            {
                if (string.IsNullOrEmpty(value))
                {
                    _queryFrequency = TimeSpan.Zero;
                }
                else
                {
                    _queryFrequency = TimeSpan.Parse(value);
                }
            }
        }

        #endregion

        #region Property Timeout

        private TimeSpan _Timeout = System.TimeSpan.FromMinutes(15);

        /// <summary>
        /// The length of time to wait for processes to finish.
        /// Default is 15 minutes.
        /// </summary>
        [TaskAttribute("timeout")]
        [StringValidator(AllowEmpty = false)]
        public string Timeout
        {
            get
            {
                return _Timeout.ToString();
            }
            set
            {
                if (string.IsNullOrEmpty(value))
                {
                    _Timeout = TimeSpan.Zero;
                }
                else
                {
                    _Timeout = TimeSpan.Parse(value);
                }
            }
        }

        #endregion

        /// <summary>
        /// Entry point for the task.
        /// </summary>
        protected override void ExecuteTask()
        {
            try
            {
                var timer = new Stopwatch();
                timer.Start();

                // figure out when we should timeout
                DateTime timeoutAtTime = DateTime.Now.Add(_Timeout);

                // query for processes
                List<Process> processes = GetProcesses();
                if (processes.Count == 0)
                {
                    // stop the timer
                    timer.Stop();

                    // we found processes matching our query
                    Log(Level.Info, "COMPLETED: No processes found to wait on. {0}", GetInputInfo());
                }
                else
                {
                    while (processes.Count > 0)
                    {
                        // do we need to timeout
                        if (timeoutAtTime < DateTime.Now)
                        {
                            throw new System.TimeoutException(string.Format("{0} process(es) did not completed within the given timeout window. {1}", processes.Count, GetInputInfo()));
                        }

                        // we found processes matching our query
                        Log(Level.Info, "WAITING: {0} process(es) found running. {1} Elapsed: {2}", processes.Count, GetInputInfo(), timer.Elapsed);

                        // we'll go to sleep for the query window of time before we wake and poll the server again
                        System.Threading.Thread.Sleep(_queryFrequency);

                        // requery for running processes
                        processes = GetProcesses();
                    }

                    // stop the timer
                    timer.Stop();

                    // we found processes matching our query
                    Log(Level.Info, "COMPLETED: All process(es) have finished execution. {1} Elapsed: {2}", processes.Count, GetInputInfo(), timer.Elapsed);
                }

            }
            catch (Exception ex)
            {
                this.ThrowOrLog(this.Location, ex);
            }
        }
        
        /// <summary>
        /// Gets all the input info into one string to log it.
        /// </summary>
        /// <returns>Returns a string with all the input info concatinated together.</returns>
        protected override string GetInputInfo()
        {
            var sb = new System.Text.StringBuilder(base.GetInputInfo());

            if (sb.Length > 0)
                sb.Append(" ");
            sb.AppendFormat(" timeout: {0}", Timeout);
            
            return sb.ToString();
        }
                
    }
}
